# module name aquamarine.py
# $Id: aquamarine.py ddcad87fab9c 2013/08/17 19:20:30 pgurumur $
# Copyright (c) 2012 Prabhu Gurumurthy <pgurumur@gmail.com>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# File aquamarine.py generated on Sunday August 26, 2012 @ 15:35:16 PDT
#

from __future__ import division

__version__  = "$Revision: ddcad87fab9c $"
__author__   = "$Author: pgurumur $"
__modified__ = "$Date: 2013-08-17 19:20:30Z $"

import sys
import math
import operator

from pyparsing import lineno

try:
   from . import exception, stdlib, node, base, functions
except ImportError:
   import exception, stdlib, node, base, functions

objects = {}
identifiers = set()

opn = {
      '+' : (lambda ix, jy: ix + jy),
      '-' : (lambda ix, jy: ix - jy),
      '*' : (lambda ix, jy: ix * jy),
      '/' : (lambda ix, jy: ix / jy),
      '^' : (lambda ix, jy: pow(ix, jy)),
      '+=' : (lambda ix, jy: operator.iadd(ix, jy)),
      '-=' : (lambda ix, jy: operator.isub(ix, jy)),
      '*=' : (lambda ix, jy: operator.imult(ix, jy)),
      '/=' : (lambda ix, jy: operator.idiv(ix, jy)),
      }

def add2namespace(function):
   def wrapper(*args, **kwargs):
      retval = function(*args, **kwargs)
      if retval is not None:
         retval.line = lineno(args[2], args[1])
         namespace = args[0].maincontext
         if isinstance(retval, (
            base.BerylFunction, base.BerylNamespace, base.BerylVariable)):
            retval.parent = namespace
            namespace += retval

         namespace.add2q(retval, retval.line)

      return retval

   return wrapper

class Compile(object):
   def __init__(self):
      super(Compile, self).__init__()
      self._lineno = 0
      self._main = base.BerylNamespace("__main__")
      self._stack = []

   def __call__(self, *args, **kwargs):
      self._main(*args, **kwargs)

   @property
   def maincontext(self):
      return self._main

   def builtins(self, String):
      if len(String.strip()) > 0:
         base.builtins.add(String)

   def addnamepsace(self, tokens, name):
      if name not in self._main:
         ns = base.BerylNamespace(tokens)
         self._main += ns
         ns.parent = self._main

   def error(self, fmt, *args):
      errmesg = None
      if len(args):
         errmesg = stdlib.sprintf(fmt, *args)
      else:
         errmesg = str(fmt)

      raise exception.CompileError(errmesg.format("ascii"))

   def indexAssignment(self, string, location, tokens):
      # retval = None
      # bassign = base.BerylAssign(tokens)
      # return bassign.validate(tokens)
      retval = None
      node = self._main.find(tokens[0])

      if len(tokens) == 1:
         retval = tokens
      else:
         start = end = index = 0
         try:
            if len(tokens) == 2:
               index = int(tokens[1])
            elif len(tokens) == 3:
               start, end = tokens[1:]
               start = int(start)
               end = int(end)
         except ValueError as err:
            raise exception.CompileError(err)

         print(start, end, index)

      return retval

   def parseString(self, string, location, tokens):
      string = tokens[0]
      string = string.replace("\"", "")
      return base.BerylVariable(str(string))

   def parseInteger(self, string, location, tokens):
      return base.BerylVariable(int(tokens[0]))

   def parseFloat(self, string, location, tokens):
      return base.BerylVariable(float(tokens[0]))

   @add2namespace
   def parsePrint(self, string, location, tokens):
      return functions.BerylPrint(tokens[1:])

   @add2namespace
   def parseAssignment(self, string, location, tokens):
      print(tokens)
      var = tokens[-1]
      var.name = tokens[0]
      return var

   def pushFirst(self, string, location, tokens):
      value = None
      if isinstance(tokens[0], str):
         value = tokens[0]
      else:
         value = tokens[0].value

      self._stack.append(value)

   @add2namespace
   def parseExpression(self, string, location, tokens):
      retval = None
      op = tokens[1]
      if op == '=':
         if len(tokens) == 3:
            retval = base.BerylVariable(tokens[-1], tokens[0])
         else:
            retval = base.BerylVariable(self._eval(), tokens[0])
      elif op in ['+=', '-=', '*=', '/=']:
         node = self._main.find(tokens[0])
         if node:
            if len(tokens) == 3:
               opn[op](node, tokens[-1])
            else:
               opn[op](node, self._eval())

            retval = node
         else:
            self.error("cannot find variable %s in line %d", tokens[0],
                  lineno(location, string))

      return retval

   def _eval(self, stack = None):
      retval = None
      slist = None
      if stack:
         slist = stack
      else:
         slist = self._stack

      op = slist.pop()
      if isinstance(op, str):
         temp = op.lower()
         if temp in ["+", "-", "*", "/", "^"]:
            op2 = self._eval(slist)
            op1 = self._eval(slist)
            retval = opn[op](op1, op2)
         elif temp == "pi":
            retval = math.pi
         elif temp == "e":
            retval = math.e
         else:
            retval = self._main.find(temp)
            if not retval:
               self.error("cannot find variable %s", temp)
      elif isinstance(op, (int, float)):
         retval = base.BerylVariable(op)

      return retval

   def validateAssignment(self, string, location, tokens):
      retval = None
      var = tokens[0]
      if var == "true":
         retval = base.BerylVariable(1)
      elif var == "false":
         retval = base.BerylVariable(0)
      else:
         retval = self._main.find(var)

      if retval is None:
         self.error("cannot find variable %s, line: %d", var, lineno(location,
            string))

      return retval

   def parseOperator(self, string, location, tokens):
      print(tokens)
      return 1

   stack = property(fget = lambda self: self._stack)
